package space.jamestang.simpleci

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.module.kotlin.readValue
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.ktor.http.*
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.sse.*
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.Options
import org.ktorm.database.Database
import org.ktorm.logging.Slf4jLoggerAdapter
import org.ktorm.support.mysql.MySqlDialect
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import space.jamestang.simpleci.config.Config
import space.jamestang.simpleci.config.ConfigChecker
import space.jamestang.simpleci.config.JacksonConfig
import space.jamestang.simpleci.config.JacksonConfig.config
import space.jamestang.simpleci.server.handler.buildLogCRUD
import space.jamestang.simpleci.server.handler.buildToolCRUD
import space.jamestang.simpleci.server.handler.projectCRUD
import space.jamestang.simpleci.server.handler.serverStatusHandler
import kotlin.io.path.Path

val globalLogger: Logger = LoggerFactory.getLogger("SimpleCI")
val globalJsonConverter = JacksonConfig.getMapper()
lateinit var dataSource: HikariDataSource
lateinit var database: Database


class SimpleCI(checker: ConfigChecker) {

    private var config: Config

    init {
        checker.check()
        config = globalJsonConverter.readValue(Path("config.json").toFile())
        establishDatabase()
    }

    // This function is the entry point of the application
    fun run() {

        val server = embeddedServer(Netty, port = 8718, host = "0.0.0.0") {
            module()
        }.start(wait = true)


        server.addShutdownHook {
            closeDatabase()
            server.stop(1000, 1000)
        }

    }


    fun Application.module() {

        install(SSE)
        install(CORS) {
            allowCredentials = true
            anyHost()
            allowHeaders { true }
            allowHeader("Content-Type")
            allowHeader("Authorization")
            allowHeader("X-Requested-With")
            allowHeader("X-Forwarded-For")
            allowHeader("X-Forwarded-Host")
            allowHeader("X-Forwarded-Proto")
            allowHeader("X-Forwarded-Port")
            allowHeader("X-Forwarded-Prefix")
            allowHeader("X-Forwarded-Host")
            allowHeader("X-Forwarded-Proto")

            allowMethod(HttpMethod.Put)
            allowMethod(HttpMethod.Post)
            allowMethod(HttpMethod.Get)
            allowMethod(HttpMethod.Delete)
        }


        install(ContentNegotiation) {
            jackson {
                enable(SerializationFeature.INDENT_OUTPUT)
                setSerializationInclusion(JsonInclude.Include.ALWAYS)
                setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
                    indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
                    indentObjectsWith(DefaultIndenter("  ", "\n"))
                })
                config("yyyy-MM-dd", "HH:mm:ss", "yyyy-MM-dd HH:mm:ss")
            }
        }

        buildToolCRUD()
        projectCRUD()
        buildLogCRUD()
        serverStatusHandler()
    }


    private fun establishDatabase() {

        val dataSourceConfig = HikariConfig().apply {
            driverClassName = "com.mysql.cj.jdbc.Driver"
            jdbcUrl = config.dbUrl
            username = config.dbUser
            password = config.dbPassword
            maximumPoolSize = config.maxConnection ?: 10
        }

        dataSource = HikariDataSource(dataSourceConfig)

        database = Database.connect(
            dataSource = dataSource,
            dialect = MySqlDialect(),
            logger = Slf4jLoggerAdapter("SimpleCI-Database")
        )
    }


    fun closeDatabase() = dataSource.close()
}


fun main(args: Array<String>) {

    val options = Options()
    options.addOption("config", true, "Load configuration file")
    options.addOption("help", false, "Prints this help message")
    options.addOption("version", false, "Prints version info")
    options.addOption("start", false, "Prints username info")


    val parser = DefaultParser()
    val cmd = parser.parse(options, args)
    if (cmd.options.size == 0) {
        println("Use --help for More Information")
    }
    if (cmd.hasOption("help")) globalLogger.info("Get more information from https://gitee.com/jamestangakajava9/simple-ci")
    if (cmd.hasOption("version")) globalLogger.info("Alpha version: 0.0.1")

    val checker = ConfigChecker
    if (cmd.hasOption("config")) {
        val config = cmd.getOptionValue("config")
        checker.setConfigLocation(config)
    }

    if (cmd.hasOption("start")) {
        val simpleCI = SimpleCI(checker)
        simpleCI.run()
    }
}
